﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Text;
using System.Threading;

namespace MyUtils
{
	public sealed class Work : IDisposable
	{
		static readonly List<Work> _works = new List<Work>();

		readonly Action _act;
		readonly ManualResetEvent _started = new ManualResetEvent(false);
		readonly ManualResetEvent _stopped = new ManualResetEvent(false);
		Thread _thread;
		readonly string _name;

		bool _disposing;

		TimeSpan _sleepBetweenActs = TimeSpan.FromMinutes(2);

		TimeSpan _sleepOnStartRandom = TimeSpan.FromSeconds(1);

		TimeSpan _sleepOnStartStatic; // = TimeSpan.FromSeconds(0);

		public Work(string name, Action act)
		{
			_name = name;
			_act = act;

			lock (_works)
			{
				_works.Add(this);
			}
		}

		public void Start()
		{
			_thread = new Thread(WorkMeth)
			{
				Name = Name,
				IsBackground = false,
				Priority = ThreadPriority.Lowest,
			};
			_thread.Start();
			_started.WaitOne();
		}

		public string Name
		{
			get { return _name; }
		}

		public TimeSpan SleepBetweenActs
		{
			get { return _sleepBetweenActs; }
			set { _sleepBetweenActs = value; }
		}

		public TimeSpan SleepOnStartRandom
		{
			get { return _sleepOnStartRandom; }
			set { _sleepOnStartRandom = value; }
		}

		public TimeSpan SleepOnStartStatic
		{
			get { return _sleepOnStartStatic; }
			set { _sleepOnStartStatic = value; }
		}

		#region IDisposable Members

		public void Dispose()
		{
			_started.Close();
			_stopped.Close();
		}

		#endregion

		public static string Info()
		{
			var sb = new StringBuilder();
			lock (_works)
			{
				foreach (var item in _works)
				{
					sb.AppendLine(item.ToString());
				}
			}
			return sb.ToString();
		}

		public static void StopAll()
		{
			foreach (var item in _works.ToArray())
			{
				item.Stop();
			}
		}

		public override string ToString()
		{
			return _name;
		}

		[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
		void WorkMeth()
		{
			try
			{
				_started.Set();
				while (true)
				{
					try
					{
						if (_disposing)
						{
							return;
						}
						Thread.Sleep((int)(_sleepOnStartStatic.TotalMilliseconds + ((int)(_sleepOnStartRandom.TotalMilliseconds)).Random()));
						if (_disposing)
						{
							return;
						}
						_act();
						if (_disposing)
						{
							return;
						}
						Thread.Sleep((int)_sleepBetweenActs.TotalMilliseconds);
					}
					catch (ThreadAbortException ex)
					{
						if (_disposing)
						{
							return;
						}
						Trace.WriteLine("Work: {0} aborted by exception. {1}".Arg(_name, ex), "ERROR");
					}
					catch (Exception ex)
					{
						Trace.WriteLine("Work: Exception: " + ex, "ERROR");
					}
				}
			}
			finally
			{
				_stopped.Set();
			}
		}

		[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
		public void Stop()
		{
			_disposing = true;
			int i;
			const int max = 10;
			Trace.WriteLine("Work: Shutdown `{0}` ...".Arg(_name));
			for (i = 1; i < max; i++)
			{
				if (_stopped.WaitOne(500, false))
				{
					break;
				}
				Trace.WriteLine("Work: Shutdown iteration `{0}`".Arg(i));
				try
				{
					_thread.Abort();
				}
				catch (Exception ex)
				{
					Trace.WriteLine("Work: Abort Failed: " + ex);
				}
			}
			if (i == max)
			{
				Trace.WriteLine("Work: Could not shutdown work `{0}` properly. See logs for more info.".Arg(_name), "ERROR");
			}
			else
			{
				Trace.WriteLine("Work: Shutdown `{0}` Complete".Arg(_name));
			}
		}
	}

	//public static class MyServiceWorks {
	//    static readonly Dictionary<string, Work> _works = new Dictionary<string,Work>();

	//    static public IEnumerable<Work> Works {
	//        get {
	//            return _works.Values;
	//        }
	//    }

	//    public static void Shutdown(Work work) {
	//    }

	//    public static void Shutdown(string name) {
	//        Work work;
	//        if (_works.TryGetValue(name, out work)) {
	//            Shutdown(work);
	//        }
	//    }

	//    public static void Create(Work work){
	//        work.Act.EnsureExists();
	//        work.Name.EnsureExists();
	//    }
	//}
}